package com.gitee.wsl.common.ui.draw.node

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.gitee.wsl.common.ui.base.Point
import com.gitee.wsl.common.ui.base.TextAlign
import com.gitee.wsl.common.ui.base.toColorOrBrush
import com.gitee.wsl.common.ui.draw.api.HasFill
import com.gitee.wsl.common.ui.draw.api.HasStroke
import com.gitee.wsl.common.ui.draw.api.HasTransform
import com.gitee.wsl.common.ui.draw.api.Transform
import com.gitee.wsl.common.ui.draw.container.GroupNode
import com.gitee.wsl.common.ui.draw.type.TextHAlign
import com.gitee.wsl.common.ui.draw.type.TextVAlign
import com.gitee.wsl.common.ui.theme.local.Web


 class TextNode(
     var x: Float = .0f,
     var y: Float = .0f
 ) : Node(), HasFill, HasStroke, HasTransform {

    override var transform: Transform? = null

     var textContent: String = "Type something"
        set(value) {
            field = value.makeVizFriendlyText()
        }

     var fontSize: Float = 12.0f
     var fontFamily: FontFamily = FontFamily.SansSerif
     var fontWeight: FontWeight = FontWeight.Normal
     var fontStyle: FontStyle= FontStyle.Normal

     override fun DrawScope.draw() {
         val offset = Offset(x.dp.toPx(),y.dp.toPx())
         textColor?.let {
             /*paint.style = Paint.Style.FILL
             it.updatePaint(paint, this)
             drawContext.canvas.nativeCanvas.drawText(
                 node.textContent,
                 node.x.dp.toPx(),
                 node.y.dp.toPx(),
                 paint
             )*/
             //drawText(,textContent,it.color,offset)
         }
         strokeColor?.let {
             /*paint.style = Paint.Style.STROKE
             it.updatePaint(paint, this)
             drawContext.canvas.nativeCanvas.drawText(
                 node.textContent,
                 node.x.dp.toPx(),
                 node.y.dp.toPx(),
                 paint
             )*/

         }
     }


 }

private fun String.makeVizFriendlyText(): String = replaceNewLineWithSpace()

private fun String.replaceNewLineWithSpace(): String  = replace('\n', ' ')


/* class FontFamily private constructor(
     val name: String) {

     companion object {
         val MONOSPACE    : FontFamily = FontFamily("monospace")
         val SANS_SERIF   : FontFamily = FontFamily("sans-serif")
         val SERIF        : FontFamily = FontFamily("serif")

         fun specifiedFont(name: String): FontFamily = FontFamily(name)
    }

    override fun toString(): String {
        return "FontFamily(name='$name')"
    }

}

 enum class FontWeight {
    BOLD,
    NORMAL,
}

 enum class FontPosture {
    ITALIC,
    NORMAL,
}*/


/**
 * Extension property to get and set all text alignments properties in one expression.
 */
 var TextNode.textAlign: TextAlign
    get() = textAlign(hAlign, vAlign)
    set(value) {
        hAlign = value.horizontal
        vAlign = value.vertical
    }

/**
 * Extension function to facilitate the alignment setting in a TextNode
 */
 fun TextNode.textAlign(
    horizontal: TextHAlign = TextHAlign.LEFT,
    vertical: TextVAlign = TextVAlign.BASELINE
): TextAlign = TextAlign(horizontal = horizontal, vertical = vertical)

/**
 * Returns the bounds of the [TextNode], no transformation is applied.
 *
 *  - the [Rect] position returned is the same as the [TextNode]
 *  - the Rect [Size] is the same as the [TextNode]
 *  - the [Rect] position may vary depending on the [TextNode] alignment properties
 *
 * @param this@measureText a [TextNode] with all properties set.
 * @param hAlign the [TextHAlign] of the text (defaults to TextHAlign.LEFT).
 * @param vAlign the [TextVAlign] of the text (defaults to TextVAlign.BASELINE).
 * @param rotation the rotation [Angle] applied to the text (defaults to 0).
 * @return A [Rect] that is the bounding box of this text on screen.
 * width.
 */
 fun TextNode.measureText(): Rect =
    measureText(
        textContent,
        fontSize,
        fontFamily,
        fontWeight,
        fontStyle,
        x, y, hAlign, vAlign
    )

/**
 * Returns the bounds of a text to be drawn on screen.
 *
 * @param text a [String] to be displayed.
 * @param font a [Font] this text uses.
 * @param xPosition the expected X position of this text as a [Float] (defaults to .0).
 * @param yPosition the expected Y position of this text as a [Float] (defaults to .0).
 * @param vAlign the [TextVAlign] of the text (defaults to TextVAlign.BASELINE).
 * @param rotation the rotation [Angle] applied to the text (defaults to 0).
 * @return A [Rect] that is the bounding box of this text on screen.
 */
 fun measureText(
    text: String,
    fontSize: Float,
    fontFamily: FontFamily,
    fontWeight: FontWeight,
    fontStyle: FontStyle,
    xPosition: Float = .0f,
    yPosition: Float = .0f,
    hAlign: TextHAlign = TextHAlign.LEFT,
    vAlign: TextVAlign = TextVAlign.BASELINE
): Rect {
    val rect: Rect = textMeasure(text,
        fontSize,
        fontFamily,
        fontWeight,
        fontStyle,
    )

    val x = xPosition + when (hAlign) {
        TextHAlign.RIGHT -> rect.left - rect.width
        TextHAlign.MIDDLE -> rect.left - (rect.width / 2.0f)
        else -> rect.left
    }
    val y = yPosition + when (vAlign) {
        TextVAlign.HANGING -> rect.top + rect.height
        TextVAlign.MIDDLE -> rect.top + (rect.height / 2.0f)
        else -> rect.top
    }
    return Rect(x, y, rect.width, rect.height)
}


fun textMeasure(text: String, fontSize: Float, fontFamily: FontFamily, fontWeight: FontWeight, fontStyle: FontStyle): Rect{
   /* val fontResolver = LocalFontFamilyResolver.current
    val density = LocalDensity.current

    val style = TextStyle(fontSize=fontSize.sp, fontFamily = fontFamily, fontWeight = fontWeight, fontStyle = fontStyle )

    val paragraph = Paragraph(
        text = text,
        style = style,
        constraints = Constraints.fixed(0,0),
        density = density,
        fontFamilyResolver = fontResolver
    )*/

    return Rect(0f,0f,0f,0f)
}



/**
 * This class is used to create rich text by composing different text nodes.
 * It manages new lignes, change of font size, font weight, font posture.
 *
 *
 * Add text nodes using the [text] function
 *
 *
 * result of the composition is the bounding size of the composed text and a
 * group that holds the different nodes.
 *
 * Use it to define a rich text block.
 *
 *     val richTextBuilder = RichTextBuilder {
 *         text("Hello "); text("Big", fontSize = 20.0, bold = true)
 *         newLine()
 *         text("World!!!", italic = true)
 *     }
 *
 * and then generate the corresponding group to add it to a [Viz].
 *
 *     val richtext = richTextBuilder.buildGroup()
 *     viz.add(richtext)
 *
 */
 class RichTextBuilder(internal val init: RichTextBuilder.() -> Unit = {}) {

    /**
     * Lazy execution of the lambda. Called only by getSize of buildGroup
     */
    private fun lazyBuild() {
        if (initialized.not()){
            init()
            initialized = true
        }
    }

    private var initialized = false


    /**
     * Builds the group containing all the rich-text nodes.
     * It's build lazily after all node have been added to the rich text builder.
     */
     fun buildGroup(): GroupNode {
        lazyBuild()
        return GroupNode().apply {
            var lineHeights = .0f
            lines.forEach { line ->
                val y =  lineHeights + line.height * .75f
                line.nodes.forEach {
                    it.y = y
                    this.add(it)
                }
                lineHeights += line.height
            }
        }
    }

     fun computeSize(): Size {
        lazyBuild()
        if (boundSize == null){
            val width = lines.maxOf { it.starts.last() }
            val height = lines.fold(0.0f) { acc: Float, line: Line ->
                acc + line.fontSize * line.lineSize
            }
            boundSize = Size(width, height)
        }
        return boundSize!!
    }

    /**
     * The bounds of the generated rich text.
     */
    private var boundSize: Size? = null


    /**
     * Defines the current line size. The line height will be
     * the max font size of the line multiplied by this value.
     */
     var lineSize: Float = 1.15f
        set(value) {
            field = value
            currentLine.lineSize = value
        }

    /**
     * The current font size, used for the nexd added text node.
     */
     var fontSize: Float = 12.0f

    /**
     * The current text color, used for the nexd added text node.
     */
     var textColor: Color = Color.Web.black

    /**
     * The current font family, used for the nexd added text node.
     */
     var fontFamily: FontFamily = FontFamily.SansSerif

    /**
     * The current fontWeight, used for the next added text node.
     */
     var fontWeight: FontWeight = FontWeight.Normal


    /**
     * The current fontStyle, used for the next added text node.
     */
     var fontStyle: FontStyle = FontStyle.Normal


    /**
     * Adds a line return
     */
     fun newLine() {
        currentLine = Line(lineSize)
        lines.add(currentLine)
    }

    /**
     * Adds a text node to the current line.
     *
     * uses the size defined by [RichTextBuilder.fontSize], unless overriden by the [fontSize] parameter.
     *
     * uses the style defined by [RichTextBuilder.fontStyle], unless overriden by the [italic] parameter.
     *
     * uses the weight defined by [RichTextBuilder.fontWeight], unless overriden by the [bold] parameter.
     *
     *     text("hello ")
     *     text("world!!", fontSize = 20.0, bold = true, italic = true, textColor = Colors.Web.red)
     */
     fun text(textContent: String,
                    fontSize: Float? = null,
                    bold: Boolean? = null,
                    italic: Boolean? = null,
                    textColor: Color? = null,
                    fontFamily: FontFamily? = null,
    ){
        val textNode = TextNode()
        textNode.textContent = textContent
        textNode.textColor = textColor?.toColorOrBrush() ?: this.textColor.toColorOrBrush()
        textNode.fontFamily = fontFamily?: this.fontFamily
        textNode.hAlign = TextHAlign.LEFT
        textNode.vAlign = TextVAlign.BASELINE
        textNode.fontSize = fontSize ?: this.fontSize
        textNode.fontWeight = when (bold) {
            null -> fontWeight
            true -> FontWeight.Bold
            false -> FontWeight.Normal
        }
        textNode.fontStyle = when (italic){
            null -> fontStyle
            true -> FontStyle.Italic
            false -> FontStyle.Normal
        }
        currentLine.add(textNode)
    }


    /**
     * Holds the different TextNodes of a line.
     */
    private inner class Line(var lineSize: Float) {
        var nodes: MutableList<TextNode> = mutableListOf()

        /**
         * Keeps the total size of already added nodes.
         */
        val starts: MutableList<Float> = mutableListOf(.0f)

        private var _fontSize = .0f
        val fontSize:Float
            get() = _fontSize

        val height: Float
            get() = fontSize * lineSize

        fun add(textNode: TextNode) {
            _fontSize = maxOf(_fontSize, textNode.fontSize)
            boundSize   = null
            textNode.x = starts.last()
            nodes.add(textNode)
            val measureText = textNode.measureText()
            starts.add(starts.last() + measureText.width)
        }

    }

    private var currentLine = Line(lineSize)
    private val lines = mutableListOf(currentLine)

}

fun GroupNode.text(text: String, point: Point = Point(), block: TextNode.(GroupNode)->Unit){
    val node = TextNode(point.x,point.y).apply { textContent = text }
    node.block(this)
    add(node)
}